﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Media;

using Styx;
using Styx.Plugins;
using Styx.Common;
using Styx.Common.Helpers;
using Styx.CommonBot.Frames;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace EtherealTheory.Honorbuddy.Plugins
{
    public class Immediate : HBPlugin
    {
        public override string Name
        {
            get
            {
                return "Immediate";
            }
        }

        public override string Author
        {
            get
            {
                return "Nightdew";
            }
        }

        public override Version Version
        {
            get
            {
                return new Version(5, 2, int.Parse("$Revision: 22 $".Split(' ')[1]));
            }
        }

        //// Updated by SVN during commit using: svn:keywords "Id Revision"
        //public override string SubversionId { get { return ("$Id: Immediate.cs 22 2013-05-07 02:49:35Z unknown@etherealtheory.com $"); } }
        //public override string SubversionRevision { get { return ("$Revision: 22 $"); } }

        public override bool WantButton
        {
            get
            {
                return true;
            }
        }

        public override string ButtonText
        {
            get
            {
                return "Settings";
            }
        }

        public override void Initialize()
        {
            Log("initializing version {0}", Version);
            base.Initialize();
        }

        public override void OnButtonPress()
        {
            new ImmediateForm().ShowDialog();
            
        }

        public override void Pulse()
        {
            if (StyxWoW.Me.IsMoving || StyxWoW.Me.IsCasting || StyxWoW.Me.Mounted || StyxWoW.Me.IsDead || StyxWoW.Me.IsGhost) 
                return;
            
            ImmediatePlugins.Pulse();
        }

        public static void Log(string format, params object[] args)
        {
            Logging.Write(Colors.SlateBlue, "[Immmediate]: " + format, args);
        }

        public static void Log(string message)
        {
            Logging.Write(Colors.SlateBlue, "[Immmediate]: " + message);
        }

        private interface IImmediatePlugin
        {
            bool Pulse();
        }

        private sealed class ImmediatePlugins : List<IImmediatePlugin>
        {
            private static readonly ImmediatePlugins _singleton = new ImmediatePlugins()
            {
                new LootingImmediatePlugin(),
                new RestackingImmediatePlugin(),
                new TidyingImmediatePlugin()
            };

            // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
            static ImmediatePlugins()
            {
            }

            private ImmediatePlugins()
            {
            }

            public static void Pulse()
            {
                foreach (IImmediatePlugin plugin in _singleton)
                    if (plugin.Pulse())
                        break;
            }
        }

        private abstract class BaseImmediatePlugin : IImmediatePlugin
        {
            #region IImmediatePlugin Members
            bool IImmediatePlugin.Pulse()
            {
                return OnPulse();
            }
            #endregion

            protected abstract bool OnPulse();
            protected virtual TimeSpan GetWaitTime()
            {
                return TimeSpan.FromMilliseconds(3000);
            }

            private WaitTimer _waitTimer;
            protected WaitTimer WaitTimer
            {
                get
                {
                    _waitTimer = null != _waitTimer ? _waitTimer : new WaitTimer(GetWaitTime());
                    return _waitTimer;
                }
            }
        }

        private class LootingImmediatePlugin : BaseImmediatePlugin
        {
            protected override TimeSpan GetWaitTime()
            {
                return TimeSpan.FromMilliseconds(1000);
            }

            protected override bool OnPulse()
            {
                if (!ImmediateSettings.Singleton.CombatLooting)
                    return false;

                if (!StyxWoW.Me.IsActuallyInCombat)
                    return false;

                if (StyxWoW.Me.HealthPercent < 50)
                    return false;

                if (StyxWoW.Me.FreeNormalBagSlots < 5)
                    return false;

                if (null != LootFrame.Instance && LootFrame.Instance.IsVisible)
                {
                    if (WaitTimer.IsFinished)
                    {
                        LootFrame.Instance.LootAll();
                        WaitTimer.Reset();
                    }
                    return true;
                }

                WoWUnit woWUnit = ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                    .Where(p => (p.Lootable) && p.Distance < p.InteractRange)
                    .OrderBy(l => l.Distance)
                    .FirstOrDefault();
                if (null == woWUnit)
                    return false;

                if (WaitTimer.IsFinished)
                {
                    Log("looting {0}", woWUnit.Name);
                    woWUnit.Interact();
                    WaitTimer.Reset();
                }
                return true;
            }
        }

        private class RestackingImmediatePlugin : BaseImmediatePlugin
        {
            protected override TimeSpan GetWaitTime()
            {
                return TimeSpan.FromMilliseconds(2000);
            }

            protected override bool OnPulse()
            {
                if (!ImmediateSettings.Singleton.InventoryRestacking)
                    return false;

                if (StyxWoW.Me.IsActuallyInCombat)
                    return false;

                if (StyxWoW.Me.FreeNormalBagSlots < 2)
                    return false;

                IList<WoWItem> stackables = StyxWoW.Me.BagItems.Where(i => i.IsValid && i.ItemInfo.MaxStackSize > 1 && i.StackCount < i.ItemInfo.MaxStackSize).ToList();
                //Need a minimum of 2 items to stack
                if(stackables.Count < 2)
                    return false;
                foreach(uint entry in stackables.ToLookup(i => i.Entry).Select(i => i.Key).Distinct())
                {
                    IList<WoWItem> entries = stackables.Where(i => i.Entry == entry).ToList();
                    //Need a minimum of 2 of the same items to stack
                    if (entries.Count() > 1)
                    {
                        if (WaitTimer.IsFinished)
                        {
                            WoWItem first = entries[0];
                            WoWItem second = entries[1];
                            Log("stacking {0} with {1} on {2} to maximize to {3}", first.Name, first.StackCount, second.StackCount, first.ItemInfo.MaxStackSize);
                            first.PickUp();
                            second.PickUp();
                            WaitTimer.Reset();
                        }
                        return true;
                    }
                }
                return false;
            }
        }

        private class TidyingImmediatePlugin : BaseImmediatePlugin
        {
            private static readonly IDictionary<uint, KeyValuePair<uint, int>> Stackables = new Dictionary<uint, KeyValuePair<uint, int>>()
            {
                { 3352, new KeyValuePair<uint, int>(3352, 1) }, //Ooze-covered Bag
                { 6351, new KeyValuePair<uint, int>(6351, 1) }, //Dented Crate
                { 6352, new KeyValuePair<uint, int>(6352, 1) }, //Waterlogged Crate
                { 6353, new KeyValuePair<uint, int>(6353, 1) }, //Small Chest
                { 6356, new KeyValuePair<uint, int>(6356, 1) }, //Battered Chest
                { 6357, new KeyValuePair<uint, int>(6357, 1) }, //Sealed Crate
                { 5523, new KeyValuePair<uint, int>(5523, 1) }, //Small Barnacled Clam
                { 5524, new KeyValuePair<uint, int>(5524, 1) }, //Thick-shelled Clam
                { 7973, new KeyValuePair<uint, int>(7973, 1) }, //Big-mouth Clam
                { 13874, new KeyValuePair<uint, int>(13874, 1) }, //Heavy Crate
                { 20708, new KeyValuePair<uint, int>(20708, 1) }, //Tightly Sealed Trunk
                { 20766, new KeyValuePair<uint, int>(20766, 1) }, //Slimy Bag
                { 20767, new KeyValuePair<uint, int>(20767, 1) }, //Scum Covered Bag
                { 20768, new KeyValuePair<uint, int>(20768, 1) }, //Oozing Bag
                { 21113, new KeyValuePair<uint, int>(21113, 1) }, //Watertight Trunk
                { 21150, new KeyValuePair<uint, int>(21150, 1) }, //Iron Bound Trunk
                { 21228, new KeyValuePair<uint, int>(21228, 1) }, //Mithril Bound Trunk
                { 21746, new KeyValuePair<uint, int>(21746, 1) }, //Lucky Red Envelope (Lunar Festival item)
                { 24476, new KeyValuePair<uint, int>(24476, 1) }, //Jaggal Clam
                { 24881, new KeyValuePair<uint, int>(24881, 1) }, //Satchel of Helpful Goods (5-15 1st)
                { 24889, new KeyValuePair<uint, int>(24889, 1) }, //Satchel of Helpful Goods (5-15 others)
                { 24882, new KeyValuePair<uint, int>(24882, 1) }, //Satchel of Helpful Goods (15-25 1st)
                { 24890, new KeyValuePair<uint, int>(24890, 1) }, //Satchel of Helpful Goods (15-25 others)
                { 27481, new KeyValuePair<uint, int>(27481, 1) }, //Heavy Supply Crate
                { 27511, new KeyValuePair<uint, int>(27511, 1) }, //Inscribed Scrollcase
                { 27513, new KeyValuePair<uint, int>(27513, 1) }, //Curious Crate
                { 32724, new KeyValuePair<uint, int>(32724, 1) }, //Sludge Covered Object
                { 35792, new KeyValuePair<uint, int>(35792, 1) }, //Mage Hunter Personal Effects
                { 35945, new KeyValuePair<uint, int>(35945, 1) }, //Brilliant Glass (Daily Cooldown for Jewelcrafting - The Burning Crusade Edition)
                { 36781, new KeyValuePair<uint, int>(36781, 1) }, //Darkwater Clam
                { 44475, new KeyValuePair<uint, int>(44475, 1) }, //Reinforced Crate
                { 44663, new KeyValuePair<uint, int>(44663, 1) }, //Abandoned Adventurer's Satchel
                { 44700, new KeyValuePair<uint, int>(44700, 1) }, //Brooding Darkwater Clam
                { 45072, new KeyValuePair<uint, int>(45072, 1) }, //Brightly Colored Egg (Noble Garden Event)
                { 45909, new KeyValuePair<uint, int>(45909, 1) }, //Giant Darkwater Clam
                { 51999, new KeyValuePair<uint, int>(51999, 1) }, //Satchel of Helpful Goods (iLevel 25)
                { 52000, new KeyValuePair<uint, int>(52000, 1) }, //Satchel of Helpful Goods (31)
                { 52001, new KeyValuePair<uint, int>(52001, 1) }, //Satchel of Helpful Goods (41)
                { 52002, new KeyValuePair<uint, int>(52002, 1) }, //Satchel of Helpful Goods (50)
                { 52003, new KeyValuePair<uint, int>(52003, 1) }, //Satchel of Helpful Goods (57)
                { 52004, new KeyValuePair<uint, int>(52004, 1) }, //Satchel of Helpful Goods (62)
                { 52005, new KeyValuePair<uint, int>(52005, 1) }, //Satchel of Helpful Goods (66)
                { 52340, new KeyValuePair<uint, int>(52340, 1) }, //Abyssal Clam
                { 61387, new KeyValuePair<uint, int>(61387, 1) }, //Hidden Stash
                { 62242, new KeyValuePair<uint, int>(62242, 1) }, //Icy Prism (Daily Cooldown for Jewelcrafting - Wrath Edition)
                { 64657, new KeyValuePair<uint, int>(64657, 1) }, //Canopic Jar (Archaeology Tol'vir relic)
                { 67248, new KeyValuePair<uint, int>(67248, 1) }, //Satchel of Helpful Goods (39)
                { 67250, new KeyValuePair<uint, int>(67250, 1) }, //Satchel of Helpful Goods (85)
                { 67495, new KeyValuePair<uint, int>(67495, 1) }, //Strange Bloated Stomach (Cataclysm Skinning)
                { 67539, new KeyValuePair<uint, int>(67539, 1) }, //Tiny Treasure Chest
                { 67597, new KeyValuePair<uint, int>(67597, 1) }, //Sealed Crate (level 85 version)
                { 69903, new KeyValuePair<uint, int>(69903, 1) }, //Satchel of Exotic Mysteries (LFD - Extra Reward)
                { 72201, new KeyValuePair<uint, int>(72201, 1) }, //Plump Intestines (MoP Skinning)
                { 73478, new KeyValuePair<uint, int>(73478, 1) }, //Fire Prism (Daily Cooldown for Jewelcrafting - Cataclysm Edition)
                { 78890, new KeyValuePair<uint, int>(78890, 1) }, //Crystalline Geode (Dragon Soul Raid - Normal 10/25 every bossloot)
                { 78891, new KeyValuePair<uint, int>(78891, 1) }, //Elementium-Coated Geode (Dragon Soul Raid - Normal 10/25 Deathwing Kill)
                { 78892, new KeyValuePair<uint, int>(78892, 1) }, //Perfect Geode (Dragon Soul Raid - Heroic Hardmode 10/25 Deathwing Kill)
                { 78897, new KeyValuePair<uint, int>(78897, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78898, new KeyValuePair<uint, int>(78898, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78899, new KeyValuePair<uint, int>(78899, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78900, new KeyValuePair<uint, int>(78900, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78901, new KeyValuePair<uint, int>(78901, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78902, new KeyValuePair<uint, int>(78902, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78903, new KeyValuePair<uint, int>(78903, 1) }, //Pouch o' Tokens (5 Darkmoon Faire Game Coins)
                { 78905, new KeyValuePair<uint, int>(78905, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78906, new KeyValuePair<uint, int>(78906, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78907, new KeyValuePair<uint, int>(78907, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78908, new KeyValuePair<uint, int>(78908, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78909, new KeyValuePair<uint, int>(78909, 1) }, //Sack o' Tokens (20 Darkmoon Faire Game Coins)
                { 78930, new KeyValuePair<uint, int>(78930, 1) }, //Sealed Crate (around the Darkmoon Faire Island)
                { 79896, new KeyValuePair<uint, int>(79896, 1) }, //Pandaren Tea Set (Archaeology)
                { 79897, new KeyValuePair<uint, int>(79897, 1) }, //Pandaren Game Board (Archaeology)
                { 79898, new KeyValuePair<uint, int>(79898, 1) }, //Twin Stein Set (Archaeology)
                { 79899, new KeyValuePair<uint, int>(79899, 1) }, //Walking Cane (Archaeology)
                { 79900, new KeyValuePair<uint, int>(79900, 1) }, //Empty Keg (Archaeology)
                { 79901, new KeyValuePair<uint, int>(79901, 1) }, //Carved Bronze Mirror (Archaeology)
                { 79902, new KeyValuePair<uint, int>(79902, 1) }, //Gold-Inlaid Figurine (Archaeology)
                { 79903, new KeyValuePair<uint, int>(79903, 1) }, //Apothecary Tins (Archaeology)
                { 79904, new KeyValuePair<uint, int>(79904, 1) }, //Pearl of Yu'lon (Archaeology)
                { 79905, new KeyValuePair<uint, int>(79905, 1) }, //Standard of Niuzao (Archaeology)
                { 79908, new KeyValuePair<uint, int>(79908, 1) }, //Manacles of Rebellion (Archaeology)
                { 79909, new KeyValuePair<uint, int>(79909, 1) }, //Cracked Mogu Runestone (Archaeology)
                { 79910, new KeyValuePair<uint, int>(79910, 1) }, //Terracotta Arm (Archaeology)
                { 79911, new KeyValuePair<uint, int>(79911, 1) }, //Petrified Bone Whip (Archaeology)
                { 79912, new KeyValuePair<uint, int>(79912, 1) }, //Thunder King Insignia (Archaeology)
                { 79913, new KeyValuePair<uint, int>(79913, 1) }, //Edicts of the Thunder King (Archaeology)
                { 79914, new KeyValuePair<uint, int>(79914, 1) }, //Iron Amulet (Archaeology)
                { 79915, new KeyValuePair<uint, int>(79915, 1) }, //Warlord's Branding Iron (Archaeology)
                { 79916, new KeyValuePair<uint, int>(79916, 1) }, //Mogu Coin (Archaeology)
                { 79917, new KeyValuePair<uint, int>(79917, 1) }, //Worn Monument Ledger (Archaeology)
                { 87217, new KeyValuePair<uint, int>(87217, 1) }, //Small Bag of Goods
                { 87391, new KeyValuePair<uint, int>(87391, 1) }, //Plundered Treasure (Luck of the Lotus Buff)
                { 88496, new KeyValuePair<uint, int>(88496, 1) }, //Sealed Crate (MoP version)
                { 89613, new KeyValuePair<uint, int>(89613, 1) }, //Cache of Treasures (Scenario Reward)
                { 89804, new KeyValuePair<uint, int>(89804, 1) }, //Cache of Mogu Riches (LFR Loot)
                { 89807, new KeyValuePair<uint, int>(89807, 1) }, //Amber Encased Treasure Pouch (LFR Loot)
                { 89808, new KeyValuePair<uint, int>(89808, 1) }, //Dividends of the Everlasting Spring (LFR Loot)
                { 89810, new KeyValuePair<uint, int>(89810, 1) }, //Bounty of a Sundered Land (LFR Contains 25G if you don't win gear)
                { 90625, new KeyValuePair<uint, int>(90625, 1) }, //Treasures of the Vale (Daily Quest Reward)
                { 90716, new KeyValuePair<uint, int>(90716, 1) }, //Good Fortune (when using a Lucky Charm on a boss for loot)
                { 90840, new KeyValuePair<uint, int>(90840, 1) }, //Marauder's Gleaming Sack of Gold (World Boss gold drop)
                { 92813, new KeyValuePair<uint, int>(92813, 1) }, //Greater Cache of Treasures (Scenario Reward)
                { 92960, new KeyValuePair<uint, int>(92960, 1) }, //Silkworm Cocoon (Tailoring Imperial Silk)
                { 95343, new KeyValuePair<uint, int>(95343, 1) }, // Treasures of the Thunder King
                { 95469, new KeyValuePair<uint, int>(95469, 1) }, // Serpent's Heart
                { 139776, new KeyValuePair<uint, int>(139776, 1) }, //Banner of the Mantid Empire (Archaeology)
                { 139779, new KeyValuePair<uint, int>(139779, 1) }, //Ancient Sap Feeder (Archaeology)
                { 139780, new KeyValuePair<uint, int>(139780, 1) }, //The Praying Mantid (Archaeology)
                { 139781, new KeyValuePair<uint, int>(139781, 1) }, //Inert Sound Beacon (Archaeology)
                { 139782, new KeyValuePair<uint, int>(139782, 1) }, //Remains of a Paragon (Archaeology)
                { 139783, new KeyValuePair<uint, int>(139783, 1) }, //Mantid Lamp (Archaeology)
                { 139784, new KeyValuePair<uint, int>(139784, 1) }, //Pollen Collector (Archaeology)
                { 139785, new KeyValuePair<uint, int>(139785, 1) }, //Kypari ap Container (Archaeology)

                { 33567, new KeyValuePair<uint, int>(33567, 5) }, //Borean Leather Scraps

                { 22572, new KeyValuePair<uint, int>(22572, 10) }, //Mote of Air
                { 22573, new KeyValuePair<uint, int>(22573, 10) }, //Mote of Earth
                { 22574, new KeyValuePair<uint, int>(22574, 10) }, //Mote of Fire
                { 22575, new KeyValuePair<uint, int>(22575, 10) }, //Mote of Life
                { 22576, new KeyValuePair<uint, int>(22576, 10) }, //Mote of Mana
                { 22577, new KeyValuePair<uint, int>(22577, 10) }, //Mote of Shadow
                { 22578, new KeyValuePair<uint, int>(22578, 10) }, //Mote of Water
                { 37700, new KeyValuePair<uint, int>(37700, 10) }, //Crystallized Air
                { 37701, new KeyValuePair<uint, int>(37701, 10) }, //Crystallized Earth
                { 37702, new KeyValuePair<uint, int>(37702, 10) }, //Crystallized Fire
                { 37703, new KeyValuePair<uint, int>(37703, 10) }, //Crystallized Shadow
                { 37704, new KeyValuePair<uint, int>(37704, 10) }, //Crystallized Life
                { 37705, new KeyValuePair<uint, int>(37705, 10) }, //Crystallized Water
                { 49655, new KeyValuePair<uint, int>(49655, 10) }, //Lovely Charm (Love is in the Air item)
                { 86547, new KeyValuePair<uint, int>(86547, 10) }, //Skyshard
                { 89112, new KeyValuePair<uint, int>(89112, 10) }, //Mote of Harmony
                { 90407, new KeyValuePair<uint, int>(90407, 10) }, //Sparkling Shard (from Prospecting ores)

                { 87686, new KeyValuePair<uint, int>(74866, 60) } // [Empty Golden Carp Container] [Golden Carp]
            };

            protected override TimeSpan GetWaitTime()
            {
                return TimeSpan.FromMilliseconds(3000);
            }

            protected override bool OnPulse()
            {
                if (!ImmediateSettings.Singleton.InventoryTidying)
                    return false;

                if (StyxWoW.Me.IsActuallyInCombat)
                    return false;

                if (StyxWoW.Me.FreeNormalBagSlots < 2)
                    return false;

                uint count;
                foreach (uint useEntry in StyxWoW.Me.BagItems.Where(i => Stackables.Keys.Contains(i.Entry)).ToLookup(i => i.Entry).Select(i => i.Key).Distinct())
                {
                    count = 0;
                    foreach (WoWItem found in StyxWoW.Me.BagItems.Where(i => i.Entry == Stackables[useEntry].Key))
                        count += found.StackCount;

                    if (count < Stackables[useEntry].Value)
                        continue;

                    if (WaitTimer.IsFinished)
                    {
                        Lua.DoString("UseItemByName(\"" + StyxWoW.Me.BagItems.First(i => i.Entry == useEntry).Name + "\")");
                        WaitTimer.Reset();
                    }
                    return true;
                }
                return false;
            }
        }
    }
}
